<?php
/**
 * @link https://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license https://www.yiiframework.com/license/
 */

namespace console\gii\controller;

use Yii;
use yii\gii\CodeFile;
use yii\helpers\Html;
use yii\helpers\Inflector;
use yii\helpers\StringHelper;

/**
 * This generator will generate a controller and one or a few action view files.
 *
 * @property-read array $actionIDs An array of action IDs entered by the user.
 * @property-read string $controllerFile The controller class file path.
 * @property-read string $controllerID The controller ID.
 * @property-read string $controllerCamelID The controller camel ID.
 * @property-read string $controllerNamespace The namespace of the controller class.
 * @property-read string $controllerSubPath The controller sub path.
 * @property-read string $modelName The model name.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @auther Clevstn
 * @since 2.0
 */
class Generator extends \yii\gii\Generator
{
    /**
     * @var string the controller class name
     */
    public $controllerClass = 'backend\controllers\DemoController';
    /**
     * @var string the base class of the controller
     */
    public $baseClass = 'builder\base\BaseController';
    /**
     * @var string list of action IDs separated by commas or spaces
     */
    public $actions = 'view create update delete sort';
    /**
     * @var string the model class name
     */
    public $modelClass = 'common\models\Demo';
    /**
     * @var string 资源名称
     */
    public $sourceName;

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'Builder(Yii Manager) Controller Generator';
    }

    /**
     * {@inheritdoc}
     */
    public function getDescription()
    {
        return 'This generator helps you to quickly generate a new builder-controller(Yii Manager) class with
            one or several controller actions.';
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return array_merge(parent::rules(), [
            [['controllerClass', 'actions', 'modelClass', 'sourceName'], 'trim'],
            [['controllerClass', 'modelClass', 'actions', 'sourceName'], 'required'],
            ['controllerClass', 'match', 'pattern' => '/^[\w\\\\]*Controller$/', 'message' => 'Only word characters and backslashes are allowed, and the class name must end with "Controller".'],
            ['controllerClass', 'validateNewClass'],
            ['actions', 'match', 'pattern' => '/^[a-z][a-z0-9\\-,\\s]*$/', 'message' => 'Only a-z, 0-9, dashes (-), spaces and commas are allowed.'],
            ['modelClass', 'fileIsExist'],
            ['sourceName', 'string', 'max' => 20],
        ]);
    }

    public function fileIsExist($attribute, $params = [])
    {
        $modelClass = $this->{$attribute};
        try {
            $modelFile = Yii::getAlias('@' . str_replace('\\', '/', trim($modelClass, '\\')) . '.php');
        } catch (\Exception $e) {
            $this->addError($attribute, $e->getMessage());
            return;
        }

        if (!is_file($modelFile)) {
            $this->addError($attribute, 'The model must already exist.');
        }
    }

    /**
     * {@inheritdoc}
     */
    public function attributeLabels()
    {
        return [
            'controllerClass' => 'Controller Class',
            'actions' => 'Action IDs',
            'modelClass' => 'Model Class',
            'sourceName' => 'Source Name'
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function requiredTemplates()
    {
        return [
            'controller.php',
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function hints()
    {
        return [
            'controllerClass' => 'This is the name of the controller class to be generated. You should
                provide a fully qualified namespaced class (e.g. <code>backend\controllers\PostController</code>),
                and class name should be in CamelCase ending with the word <code>Controller</code>. Make sure the class
                is using the same namespace as specified by your application\'s controllerNamespace property.',
            'actions' => 'Provide one or multiple action IDs to generate empty action method(s) in the controller. Separate multiple action IDs with commas or spaces.
                Action IDs should be in lower case. For example:
                <ul>
                    <li><code>index</code> generates <code>actionIndex()</code></li>
                    <li><code>create-order</code> generates <code>actionCreateOrder()</code></li>
                </ul>',
            'modelClass' => '模型class必须是完整的,用来生成控制器方法中的逻辑,如: <code>common\models\User</code>',
            'sourceName' => '资源名称, 如: 控制器 <code>MemberController.php</code> 资源名称 <code>会员</code>,用于生成页面<code>title</code>',
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function successMessage()
    {
        return 'The controller has been generated successfully.' . $this->getLinkToTry();
    }

    /**
     * This method returns a link to try controller generated
     * @see https://github.com/yiisoft/yii2-gii/issues/182
     * @return string
     * @since 2.0.6
     */
    private function getLinkToTry()
    {
        if (strpos($this->controllerNamespace, Yii::$app->controllerNamespace) !== 0) {
            return '';
        }

        $actions = $this->getActionIDs();
        if (in_array('index', $actions, true)) {
            $route = $this->getControllerSubPath() . $this->getControllerID() . '/index';
        } else {
            $route = $this->getControllerSubPath() . $this->getControllerID() . '/' . reset($actions);
        }
        return ' You may ' . Html::a('try it now', Yii::$app->getUrlManager()->createUrl($route), ['target' => '_blank', 'rel' => 'noopener noreferrer']) . '.';
    }

    /**
     * {@inheritdoc}
     */
    public function generate()
    {
        $files = [];

        $files[] = new CodeFile(
            $this->getControllerFile(),
            $this->render('controller.php')
        );

        return $files;
    }

    /**
     * {@inheritdoc}
     */
    public function stickyAttributes()
    {
        return [];
    }

    /**
     * Normalizes [[actions]] into an array of action IDs.
     * @return array an array of action IDs entered by the user
     */
    public function getActionIDs()
    {
        $actions = array_unique(preg_split('/[\s,]+/', $this->actions, -1, PREG_SPLIT_NO_EMPTY));
        //sort($actions);

        return $actions;
    }

    /**
     * @return string the controller class file path
     */
    public function getControllerFile()
    {
        return Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->controllerClass, '\\'))) . '.php';
    }

    /**
     * @return string the controller ID
     */
    public function getControllerID()
    {
        $name = StringHelper::basename($this->controllerClass);
        return Inflector::camel2id(substr($name, 0, strlen($name) - 10));
    }

    /**
     * @return string the controller camel ID
     */
    public function getControllerCamelID()
    {
        return Inflector::id2camel($this->controllerID);
    }

    /**
     * This method will return sub path for controller if it
     * is located in subdirectory of application controllers dir
     * @see https://github.com/yiisoft/yii2-gii/issues/182
     * @since 2.0.6
     * @return string the controller sub path
     */
    public function getControllerSubPath()
    {
        $subPath = '';
        $controllerNamespace = $this->getControllerNamespace();
        if (strpos($controllerNamespace, Yii::$app->controllerNamespace) === 0) {
            $subPath = substr($controllerNamespace, strlen(Yii::$app->controllerNamespace));
            $subPath = ($subPath !== '') ? str_replace('\\', '/', substr($subPath, 1)) . '/' : '';
        }
        return $subPath;
    }

    /**
     * @return string the namespace of the controller class
     */
    public function getControllerNamespace()
    {
        $name = StringHelper::basename($this->controllerClass);
        return ltrim(substr($this->controllerClass, 0, - (strlen($name) + 1)), '\\');
    }

    /**
     * @return string the model name
     */
    public function getModelName()
    {
        return StringHelper::basename($this->modelClass);
    }
}
